//
// Created by QU on 24-6-24.
//
#include <iostream>
#include <functional>
#include <utility>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include <new>
#include <thread>
#include <algorithm>
#include <array>
#include <string>
#include <deque>
#include <thread>
#include <mutex>

using std::cout, std::cin, std::endl;

/**
 *  线程局部存储是指对象内存在线程开始后分配，线程结束时回收且每个线程有该对象自己的实例，
 *  简单地说，线程局部存储的对象都是独立于各个线程的。
 *  实际上，这并不是一个新鲜的概念，虽然C++一直没有在语言层面支持它，但是很早之前操作系统就有办法支持线程局部存储了。
 *
 *  thread_local说明符用于声明在线程生命周期内存储的对象，每个线程都有自己的对象副本，而不是所有线程共享一个对象。
 *
 *  在C++11标准确定之前，各个编译器也用了自定义的方法支持线程局部存储。比如gcc和clang添加了关键字__thread来声明线程局
 *  部存储变量，而Visual Studio C++则是使用__declspec(thread)。虽然它们都有各自的方法声明线程局部存
 *  储变量，但是其使用范围和规则却存在一些区别，这种情况增加了C++的学习成本，也是C++标准委员会不愿意看到的。
 *  于是在C++11标准中正式添加了新的thread_local说明符来声明线程局部存储变量。
 */

// thread_local说明符
// thread_local说明符可以用来声明线程生命周期的对象，它能与static或extern结合，分别指定内部或外部链接，不过额外的
// static并不影响对象的生命周期。 换句话说，static并不影响其线程局部存储的属性：
//////////  static并不影响其线程局部存储的属性  ////////   是什么意思
/**
 * 被thread_local修饰的变量在每个线程中都有自己独立的存储空间，并且在该线程的整个生命周期内都存在。
 * thread_local变量本质上是静态的，即它们在首次使用时初始化，并且在线程结束之前保持其状态，不会因为超出作用域而被销毁。
 *
 * 解释：
 * thread_local变量：每个线程都有自己独立的副本，这些副本在整个线程的生命周期内保持其状态，不会因为函数返回或超出作用域而被销毁。
 * 这些变量在首次使用时初始化，且在同一线程内保持其状态。
 *
 * static thread_local变量：static关键字在这种情况下只是控制变量的链接性，使其在翻译单元内可见。
 * 对于线程局部存储的特性，static并不增加新的行为。
 *
 * thread_local 变量的初始化时机是在每个线程首次访问该变量时，生命周期贯穿整个线程的生命期。
 * 全局 thread_local 变量和局部 thread_local 变量都是如此，区别在于定义位置和作用范围。
 */

/**
 * 在了解了线程局部存储的意义之后，让我们回头仔细阅读其定义，会发现线程局部存储只是定义了对象的生命周期，而没有定义可
 * 访问性。也就是说，我们可以获取线程局部存储变量的地址并将其传递给其他线程，并且其他线程可以在其生命周期内自由使用变量。不
 * 过这样做除了用于诊断功能以外没有实际意义，而且其危险性过大，一旦没有掌握好目标线程的声明周期，就很可能导致内存访问异常，
 * 造成未定义的程序行为，通常情况下是程序崩溃。
 *
 * 值得注意的是，使用取地址运算符&取到的线程局部存储变量的地址是运行时被计算出来的，它不是一个常量，也就是说无法和constexpr结合.
 */

thread_local int therad_local_variable;
static int static_variable;

struct X
{
    thread_local static int i;
    X() { std::cout << "X constructed in thread " << std::this_thread::get_id() << std::endl; }
    ~X() { std::cout << "X destructed in thread " << std::this_thread::get_id() << std::endl; }
};

thread_local X a;

void thread_function()
{
    std::cout << "In thread function" << std::endl;
}

int main_switch(int argc, const char* argv[])
{
    constexpr int* static_variable_p = &static_variable; // 编译成功，static_variable的地址在编译时确定
    //constexpr int *therad_local_variable_p = &therad_local_variable;    // 编译失败，therad_local_variable的地址在运行时确定

    thread_local X b; // 定义一个在线程中的独立的变量
    std::cout << "In main function" << std::endl;

    std::thread t(thread_function);

    t.join();

    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////
// 代码并发3个工作线程，前两个线程threadfunc1和threadfunc2分别调用了3次foo函数。
// 而第三个线程threadfunc3调用了1次foo函数和2次bar函数。
// 其中foo和bar函数的功能相似，它们分别声明并初始化了一个线程局部存储对象tv，并调用其自增函数inc，而inc函数会递增对象成员变量i。
// 为了保证输出的日志不会受到线程竞争的干扰，在输出之前加了互斥锁。

std::mutex g_out_lock;

struct RefCount
{
    RefCount(const char* f) : i(0), func(f)
    {
        std::lock_guard<std::mutex> lock(g_out_lock);
        std::cout << std::this_thread::get_id()
            << "|" << func
            << " : ctor i(" << i << ")" << std::endl;
    }

    ~RefCount()
    {
        std::lock_guard<std::mutex> lock(g_out_lock);
        std::cout << std::this_thread::get_id()
            << "|" << func
            << " : dtor i(" << i << ")" << std::endl;
    }

    void inc()
    {
        std::lock_guard<std::mutex> lock(g_out_lock);
        std::cout << std::this_thread::get_id()
            << "|" << func
            << " : ref count add 1 to i(" << i << ")" <<
            std::endl;
        i++;
    }

    int i;
    std::string func;
};

RefCount* lp_ptr = nullptr;

void foo(const char* f)
{
    std::string func(f);
    thread_local RefCount tv(func.append("#foo").c_str());
    tv.inc();
}

void bar(const char* f)
{
    std::string func(f);
    thread_local RefCount tv(func.append("#bar").c_str());
    tv.inc();
}

void threadfunc1()
{
    const char* func = "threadfunc1";
    foo(func);
    foo(func);
    foo(func);
}

void threadfunc2()
{
    const char* func = "threadfunc2";
    foo(func);
    foo(func);
    foo(func);
}

void threadfunc3()
{
    const char* func = "threadfunc3";
    foo(func);
    bar(func);
    bar(func);
}

int main()
{
    std::thread t1(threadfunc1);
    std::thread t2(threadfunc2);
    std::thread t3(threadfunc3);
    t1.join();
    t2.join();
    t3.join();
}
